package aos.voting;

import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

import aos.voting.entity.Configuration;
import aos.voting.entity.Message;
import aos.voting.entity.ReportData;
import aos.voting.exception.AosException;
import aos.voting.socket.SocketHandler;
import aos.voting.socket.SocketListener;

public abstract class NetworkNode implements Runnable {
	protected int nodeId;
	protected Configuration config;
	protected Queue<Message> messageQueue;
	protected HashMap<Integer, SocketHandler> id2Socket;
	protected SocketListener tcpServer;
	protected ReportData report;

	public NetworkNode(Configuration config) throws AosException {
		this.config = config;
		nodeId = config.getLocalNodeId();

		messageQueue = new LinkedList<Message>();
		report = new ReportData(nodeId);

		new Thread(this).start();

		id2Socket = new HashMap<Integer, SocketHandler>();
		tcpServer = new SocketListener(config.getPort(nodeId), id2Socket,
				config, messageQueue, report);
		new Thread(tcpServer).start();
	}

	public int getNodeId() {
		return nodeId;
	}

	protected abstract void handleMessageReceive(Message msg)
			throws AosException;

	public synchronized void sendMessage(Message msg) throws AosException {
		SocketHandler socketHandler = null;
		socketHandler = id2Socket.get(msg.getToId());
		if (socketHandler == null) {
			Socket socket = null;
			try {
				socket = new Socket(config.getIpAddress(msg.getToId()),
						config.getPort(msg.getToId()));
			} catch (IOException e) {
				throw new AosException(e.getMessage());
			}
			socketHandler = new SocketHandler(socket, messageQueue, report);
			id2Socket.put(msg.getToId(), socketHandler);
			new Thread(socketHandler).start();
		}
		socketHandler.sendMessage(msg);
	}

	@Override
	public void run() {
		while (true) {
			synchronized (messageQueue) {
				while (!messageQueue.isEmpty()) {
					Message msg = messageQueue.poll();
					try {
						handleMessageReceive(msg);
					} catch (AosException e) {
						e.printStackTrace();
					}
				}
				try {
					messageQueue.wait(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
